package spark.graphics;

import spark.math.Vector3;
import spark.math.Matrix;
import spark.math.Geometry;

import javax.media.opengl.GL;
import javax.media.opengl.glu.GLU;

public class Camera {
	private final Vector3 worldUp = new Vector3(0, 1, 0);
	private final Vector3 worldRight = new Vector3(-1, 0, 0);
	private final Vector3 worldForward = new Vector3(0, 0, -1);
	private Vector3 position;
	private Vector3 up;
	private Vector3 right;
	private Vector3 forward;
	private float angleYaw;
	private float anglePitch;
	private float angleRoll;
	private Matrix matrixYawPitchRotation;
	private float zNearPlane;
	private float zFarPlane;
	private float pitchLock;

	public Camera() {
		reset();
	}

	public void reset() {
		position = new Vector3();
		up = new Vector3(worldUp);
		right = new Vector3(worldRight);
		forward = new Vector3(worldForward);
		matrixYawPitchRotation = Matrix.createIdentity();
		pitchLock = 90.0f;
		angleYaw = 0.0f;
		anglePitch = 0.0f;
		angleRoll = 0.0f;
	}

	public static void setProjectionPerspectiveFov(GL gl, GLU glu, float aFieldOfViewY, float aAspectRatio, float aZNearPlane, float aZFarPlane) {
		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();
		glu.gluPerspective(aFieldOfViewY, (double) aAspectRatio, aZNearPlane, aZFarPlane);
		gl.glMatrixMode(GL.GL_MODELVIEW);
	}

	public static void setProjectionOrtho(GL gl, GLU glu, float left, float top, float width, float height) {
		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();
		glu.gluOrtho2D(left, left + width, top + height, top);
		gl.glScalef(1, -1, 1);
		//gl.glTranslatef(0, -height, 0);
		gl.glMatrixMode(GL.GL_MODELVIEW);
	}

	public void strafe(float aUnits) {
		position.add(Vector3.multiply(right, aUnits));
	}

	public void walk(float aUnits) {
		position.add(Vector3.multiply(forward, aUnits));
	}

	public void jump(float aUnits) {
		position.add(Vector3.multiply(up, aUnits));
	}

	public float getZNearPlane() {
		return zNearPlane;
	}

	public float getZFarPlane() {
		return zFarPlane;
	}

	public float getYaw() {
		return angleYaw;
	}

	public void setYaw(float value) {
		angleYaw = value;

		matrixYawPitchRotation = Matrix.createRotationYawPitchRoll(Geometry.degreeToRadian(angleYaw), Geometry.degreeToRadian(anglePitch), Geometry.degreeToRadian(angleRoll));
		right = Vector3.transformCoordinate(worldRight, matrixYawPitchRotation);
		forward = Vector3.transformCoordinate(worldForward, matrixYawPitchRotation);
	}

	public float getPitch() {
		return anglePitch;
	}

	public void setPitch(float value) {
		if(value > pitchLock) {
			anglePitch = pitchLock;
			return;
		}
		if(value < -pitchLock) {
			anglePitch = -pitchLock;
			return;
		}
		anglePitch = value;
		matrixYawPitchRotation = Matrix.createRotationYawPitchRoll(Geometry.degreeToRadian(angleYaw), Geometry.degreeToRadian(anglePitch), Geometry.degreeToRadian(angleRoll));
		up = Vector3.transformCoordinate(worldUp, matrixYawPitchRotation);
		forward = Vector3.transformCoordinate(worldForward, matrixYawPitchRotation);
	}

	public float getRoll() {
		return angleRoll;
	}

	public void setRoll(float value) {
		angleRoll = value;
		matrixYawPitchRotation = Matrix.createRotationYawPitchRoll(Geometry.degreeToRadian(angleYaw), Geometry.degreeToRadian(anglePitch), Geometry.degreeToRadian(angleRoll));
		up = Vector3.transformCoordinate(worldUp, matrixYawPitchRotation);
		right = Vector3.transformCoordinate(worldRight, matrixYawPitchRotation);
	}

	public void update() {
		Vector3 center = Vector3.add(position, forward);
		Renderer.getInstance().getGlu().gluLookAt(
				position.x, position.y, position.z,
				center.x, center.y, center.z,
				up.x, up.y, up.z);
	}
}
